﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCacheDataTemplate.h
//    @Author           :    Gao.Yi
//    @Date             :    2025-08-14
//    @Email			:    445267987@qq.com
//    @Module           :    NFCacheDataTemplate
//    @Desc             :    缓存数据模板类
//                           基于模板模式封装缓存数据对象，支持不同Protobuf类型的复用
//                           提供Protobuf消息与缓存数据结构的自动转换
//
// -------------------------------------------------------------------------

#pragma once

#include "NFCacheData.h"
#include "NFCacheDataWithSaveDb.h"

/**
 * @tparam className 具体缓存数据类名（CRTP模式）
 * @tparam pbClass   缓存数据对应的Protobuf类型
 * @tparam pbClassStruct 缓存数据的内部数据结构类型
 * @tparam classType 共享内存对象类型枚举
 * 
 * @brief 缓存数据模板类
 * 
 * 基于模板模式封装缓存数据对象的完整功能：
 * 1. 数据存储：提供内部数据结构存储缓存数据
 * 2. 数据转换：支持Protobuf消息与内部数据结构的双向转换
 * 3. 生命周期管理：继承基类的过期时间管理功能
 * 4. 类型安全：通过模板参数确保类型安全
 * 
 * 使用方式：
 * - 继承此类并指定具体的Protobuf类型和数据结构类型
 * - 通过 GetData() 方法访问内部数据结构
 * - 支持从Protobuf消息自动初始化数据
 * 
 * 设计特点：
 * - 使用CRTP模式实现编译时多态
 * - 通过模板参数支持不同Protobuf类型的复用
 * - 提供类型安全的数据访问接口
 * - 自动处理Protobuf消息的解析和转换
 */
template <typename className, typename pbClass, typename pbClassStruct, int classType>
class NFCacheDataTemplate : public NF_OBJECT_TEMPLATE_MACRO(className, classType, NFCacheData)
public:
    /**
     * @brief 构造函数，根据共享内存模式进行初始化
     * 
     * 根据共享内存管理器的创建模式选择初始化方式：
     * - EN_OBJ_MODE_INIT：冷启动模式，调用 CreateInit() 进行完整初始化
     * - 其他模式：热启动模式，调用 ResumeInit() 进行恢复初始化
     */
    NFCacheDataTemplate()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    /**
     * @brief 析构函数
     * 
     * 清理资源，由于使用共享内存管理，通常无需特殊处理
     */
    ~NFCacheDataTemplate() override
    {
    }

    /**
     * @brief 冷启动创建初始化
     * 
     * 在共享内存冷启动场景下，对新创建的缓存数据对象进行初始化
     * 当前实现为空，如有需要可在此添加初始化逻辑
     * 
     * @return 0 表示成功
     */
    int CreateInit()
    {
        return 0;
    }

    /**
     * @brief 热启动恢复初始化
     * 
     * 在共享内存热启动场景下，从持久化数据恢复缓存数据状态时调用
     * 当前实现为空，如有需要可在此添加恢复逻辑
     * 
     * @return 0 表示成功
     */
    int ResumeInit()
    {
        return 0;
    }

public:
    /**
     * @brief 获取内部数据结构的引用（可修改）
     * 
     * 返回内部数据结构的引用，允许直接修改缓存数据。
     * 适用于需要更新缓存数据的场景。
     * 
     * @return 内部数据结构的引用
     */
    pbClassStruct& GetData() { return m_stData; }

    /**
     * @brief 获取内部数据结构的常量引用（只读）
     * 
     * 返回内部数据结构的常量引用，只允许读取缓存数据。
     * 适用于只读访问场景，提供更好的类型安全。
     * 
     * @return 内部数据结构的常量引用
     */
    const pbClassStruct& GetData() const { return m_stData; }

    /**
     * @brief 获取 Protobuf 数据对象
     *
     * 此方法将内部数据结构转换为对应的 Protobuf 数据对象并返回。
     * 使用场景包括需要将缓存数据以 Protobuf 格式传输或存储时。
     *
     * @return Protobuf 数据对象
     */
    pbClass GetPbData() const { return m_stData.ToPb(); }

public:
    /**
     * @brief 从Protobuf消息初始化数据
     * 
     * 实现从Protobuf消息到内部数据结构的转换：
     * 1. 如果消息为空，调用 InitConfig() 进行配置初始化
     * 2. 如果消息不为空，验证消息类型并调用 InitData() 进行数据初始化
     * 3. 支持类型安全的Protobuf消息处理
     * 
     * @param pMessage 指向Protobuf消息对象的指针，可以为nullptr
     * @return 0 表示成功，其他值为错误码
     */
    int InitDataFromMessage(const google::protobuf::Message* pMessage) override
    {
        if (pMessage == nullptr)
        {
            // 消息为空时进行配置初始化
            this->InitConfig();
        }
        else
        {
            // 验证消息类型并进行数据初始化
            const pbClass* pPbClass = dynamic_cast<const pbClass*>(pMessage);
            CHECK_NULL_WF(this->GetCacheId(), pPbClass, "pMessage is not pbClass:{} type, pMessage:{}", typeid(pbClass).name(), pMessage->GetTypeName());
            int iRetCode = InitData(pPbClass);
            CHECK_ERR(this->GetCacheId(), iRetCode, "InitData Failed");
        }
        return 0;
    }

    /**
     * @brief 从Protobuf消息初始化数据（虚函数）
     * 
     * 子类可以重写此方法实现具体的Protobuf数据初始化逻辑。
     * 基类实现使用 FromPb() 方法进行自动转换。
     * 
     * @param pPbClass 指向Protobuf消息对象的指针
     * @return 0 表示成功，其他值为错误码
     */
    virtual int InitData(const pbClass* pPbClass)
    {
        CHECK_NULL(this->GetCacheId(), pPbClass);
        // 使用FromPb方法将Protobuf数据转换为内部数据结构
        m_stData.FromPb(*pPbClass);
        return 0;
    }

protected:
    /** @brief 内部数据结构，存储实际的缓存数据 */
    pbClassStruct m_stData;
};


template <typename className, typename pbClass, typename pbClassStruct, int classType>
class NFCacheDataWithSaveDbTemplate : public NF_OBJECT_TEMPLATE_MACRO(className, classType, NFCacheDataWithSaveDb)
public:
    /**
     * @brief 构造函数，根据共享内存模式进行初始化
     *
     * 根据共享内存管理器的创建模式选择初始化方式：
     * - EN_OBJ_MODE_INIT：冷启动模式，调用 CreateInit() 进行完整初始化
     * - 其他模式：热启动模式，调用 ResumeInit() 进行恢复初始化
     */
    NFCacheDataWithSaveDbTemplate()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    /**
     * @brief 析构函数
     *
     * 清理资源，由于使用共享内存管理，通常无需特殊处理
     */
    ~NFCacheDataWithSaveDbTemplate() override
    {
    }

    /**
     * @brief 冷启动创建初始化
     *
     * 在共享内存冷启动场景下，对新创建的缓存数据对象进行初始化
     * 当前实现为空，如有需要可在此添加初始化逻辑
     *
     * @return 0 表示成功
     */
    int CreateInit()
    {
        return 0;
    }

    /**
     * @brief 热启动恢复初始化
     *
     * 在共享内存热启动场景下，从持久化数据恢复缓存数据状态时调用
     * 当前实现为空，如有需要可在此添加恢复逻辑
     *
     * @return 0 表示成功
     */
    int ResumeInit()
    {
        return 0;
    }

public:
    /**
     * @brief 获取内部数据结构的引用（可修改）
     *
     * 返回内部数据结构的引用，允许直接修改缓存数据。
     * 适用于需要更新缓存数据的场景。
     *
     * @return 内部数据结构的引用
     */
    pbClassStruct& GetData() { return m_stData; }

    /**
     * @brief 获取内部数据结构的常量引用（只读）
     *
     * 返回内部数据结构的常量引用，只允许读取缓存数据。
     * 适用于只读访问场景，提供更好的类型安全。
     *
     * @return 内部数据结构的常量引用
     */
    const pbClassStruct& GetData() const { return m_stData; }

    /**
     * @brief 获取 Protobuf 数据对象
     *
     * 此方法将内部数据结构转换为对应的 Protobuf 数据对象并返回。
     * 使用场景包括需要将缓存数据以 Protobuf 格式传输或存储时。
     *
     * @return Protobuf 数据对象
     */
    pbClass GetPbData() const { return m_stData.ToPb(); }

public:
    /**
     * @brief 从Protobuf消息初始化数据
     *
     * 实现从Protobuf消息到内部数据结构的转换：
     * 1. 如果消息为空，调用 InitConfig() 进行配置初始化
     * 2. 如果消息不为空，验证消息类型并调用 InitData() 进行数据初始化
     * 3. 支持类型安全的Protobuf消息处理
     *
     * @param pMessage 指向Protobuf消息对象的指针，可以为nullptr
     * @return 0 表示成功，其他值为错误码
     */
    int InitDataFromMessage(const google::protobuf::Message* pMessage) override
    {
        if (pMessage == nullptr)
        {
            // 消息为空时进行配置初始化
            this->InitConfig();
        }
        else
        {
            // 验证消息类型并进行数据初始化
            const pbClass* pPbClass = dynamic_cast<const pbClass*>(pMessage);
            CHECK_NULL_WF(this->GetCacheId(), pPbClass, "pMessage is not pbClass:{} type, pMessage:{}", typeid(pbClass).name(), pMessage->GetTypeName());
            int iRetCode = InitData(pPbClass);
            CHECK_ERR(this->GetCacheId(), iRetCode, "InitData Failed");
        }
        return 0;
    }

    /**
     * @brief 从Protobuf消息初始化数据（虚函数）
     *
     * 子类可以重写此方法实现具体的Protobuf数据初始化逻辑。
     * 基类实现使用 FromPb() 方法进行自动转换。
     *
     * @param pPbClass 指向Protobuf消息对象的指针
     * @return 0 表示成功，其他值为错误码
     */
    virtual int InitData(const pbClass* pPbClass)
    {
        CHECK_NULL(this->GetCacheId(), pPbClass);
        // 使用FromPb方法将Protobuf数据转换为内部数据结构
        m_stData.FromPb(*pPbClass);
        return 0;
    }

    virtual int SaveData(pbClass* pPbClass)
    {
        CHECK_NULL(this->GetCacheId(), pPbClass);
        m_stData.ToPb(pPbClass);
        return 0;
    }

protected:
    /** @brief 内部数据结构，存储实际的缓存数据 */
    pbClassStruct m_stData;
};